<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 4.2.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/font-awesome.min.css">


<script id="hexo-configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    hostname: new URL('http://yoursite.com').hostname,
    root: '/',
    scheme: 'Pisces',
    version: '7.6.0',
    exturl: false,
    sidebar: {"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},
    copycode: {"enable":false,"show_result":false,"style":null},
    back2top: {"enable":true,"sidebar":false,"scrollpercent":false},
    bookmark: {"enable":false,"color":"#222","save":"auto"},
    fancybox: false,
    mediumzoom: false,
    lazyload: false,
    pangu: false,
    comments: {"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},
    algolia: {
      appID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    },
    localsearch: {"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},
    path: '',
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}
  };
</script>

  <meta name="description" content="Overview天啊，怎么改了几行代码就改出了一个惊天大的大Bug。居然导致死锁了，这是怎么回事呢，且让我一一道来。 https:&#x2F;&#x2F;blog.csdn.net&#x2F;hxlawf&#x2F;article&#x2F;details&#x2F;98961011">
<meta property="og:type" content="article">
<meta property="og:title" content="信号安全函数">
<meta property="og:url" content="http://yoursite.com/2021/06/07/%E4%BF%A1%E5%8F%B7%E5%AE%89%E5%85%A8%E5%87%BD%E6%95%B0/index.html">
<meta property="og:site_name" content="SRCNN">
<meta property="og:description" content="Overview天啊，怎么改了几行代码就改出了一个惊天大的大Bug。居然导致死锁了，这是怎么回事呢，且让我一一道来。 https:&#x2F;&#x2F;blog.csdn.net&#x2F;hxlawf&#x2F;article&#x2F;details&#x2F;98961011">
<meta property="og:locale" content="en_US">
<meta property="article:published_time" content="2021-06-07T14:49:28.000Z">
<meta property="article:modified_time" content="2021-07-08T15:25:09.977Z">
<meta property="article:author" content="Z.J. Jiang">
<meta property="article:tag" content="SISR">
<meta property="article:tag" content=" FH">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="http://yoursite.com/2021/06/07/%E4%BF%A1%E5%8F%B7%E5%AE%89%E5%85%A8%E5%87%BD%E6%95%B0/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome: false,
    isPost: true
  };
</script>

  <title>信号安全函数 | SRCNN</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-meta">

    <div>
      <a href="/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">SRCNN</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
        <p class="site-subtitle">SISR-FH</p>
  </div>

  <div class="site-nav-toggle">
    <div class="toggle" aria-label="Toggle navigation bar">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>
</div>


<nav class="site-nav">
  
  <ul id="menu" class="menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-fw fa-home"></i>Home</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="fa fa-fw fa-tags"></i>Tags</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-fw fa-th"></i>Categories</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="fa fa-fw fa-archive"></i>Archives</a>

  </li>
  </ul>

</nav>
</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content">
            

  <div class="posts-expand">
      
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block " lang="en">
    <link itemprop="mainEntityOfPage" href="http://yoursite.com/2021/06/07/%E4%BF%A1%E5%8F%B7%E5%AE%89%E5%85%A8%E5%87%BD%E6%95%B0/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="Z.J. Jiang">
      <meta itemprop="description" content="about the single image super-resolution and face hallucination">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="SRCNN">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          信号安全函数
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              <span class="post-meta-item-text">Posted on</span>

              <time title="Created: 2021-06-07 22:49:28" itemprop="dateCreated datePublished" datetime="2021-06-07T22:49:28+08:00">2021-06-07</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                <span class="post-meta-item-text">Edited on</span>
                <time title="Modified: 2021-07-08 23:25:09" itemprop="dateModified" datetime="2021-07-08T23:25:09+08:00">2021-07-08</time>
              </span>

          

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <h1 id="Overview"><a href="#Overview" class="headerlink" title="Overview"></a>Overview</h1><p>天啊，怎么改了几行代码就改出了一个惊天大的大Bug。居然导致死锁了，这是怎么回事呢，且让我一一道来。</p>
<p><a href="https://blog.csdn.net/hxlawf/article/details/98961011" target="_blank" rel="noopener">https://blog.csdn.net/hxlawf/article/details/98961011</a><br><a id="more"></a></p>
<h1 id="信号"><a href="#信号" class="headerlink" title="信号"></a>信号</h1><p>我们首先需要知道信号的概念。<strong>信号</strong>就是一条小消息，它通知进程系统中发生一个某种类型的事件。信号提供了某种机制，通知用户进程发生了某些异常。</p>
<p>信号是很短的消息，可以被发送到一个进程或一组进程。信号是事件发生时对进程的通知机制，有时也称之为软件中断。信号与硬件中断的相似之处在于打断了程序执行的正常流程，大多数情况下，无法预测信号到达的精确时间。一般来说，使用sigaction设置信号处理程序，当收到信号时，会调用信号处理器程序，可能会随时打断主程序流程；内核代表进程来调用处理器程序，当处理器返回时，主程序会在处理器打断的位置恢复执行。与 signal()相比， sigaction()系统调用在设置信号处置方面提供了更多控制，且更具灵活性。signal有可移植性的风险，尽量使用sigaction来设置信号处理程序。</p>
<p>20-1的图贴上去。<br>archlinux打印出来的kill -l打印出来的信号<br> 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP<br> 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1<br>11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM<br>16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP<br>21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ<br>26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR<br>31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3<br>38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8<br>43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13<br>48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12<br>53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7<br>58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2<br>63) SIGRTMAX-1  64) SIGRTMAX</p>
<p>1~31号为常规信号，同种类型的常规信号并不排队，如果一个常规信号被连续发送多次，那么，只有其中的一个发送到接收进程。。32~64为实时信号。实时信号与常规信号有很大不同，因为它们必须排队以便发送的多个信号都能被接收到。前者为标准信号。后者为实时信号。</p>
<p>一般而言，将信号处理函数设计得越简单越好。这将降低引发竞争条件的风险。</p>
<h1 id="信号基本概念"><a href="#信号基本概念" class="headerlink" title="信号基本概念"></a>信号基本概念</h1><h1 id="进程中使用信号"><a href="#进程中使用信号" class="headerlink" title="进程中使用信号"></a>进程中使用信号</h1><h1 id="线程中使用信号。"><a href="#线程中使用信号。" class="headerlink" title="线程中使用信号。"></a>线程中使用信号。</h1><h1 id="如何编写合适的信号处理函数"><a href="#如何编写合适的信号处理函数" class="headerlink" title="如何编写合适的信号处理函数"></a>如何编写合适的信号处理函数</h1><h1 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h1><p>死锁风险<br>可重入性问题。</p>
<h2 id="信号术语"><a href="#信号术语" class="headerlink" title="信号术语"></a>信号术语</h2><h2 id="发送信号"><a href="#发送信号" class="headerlink" title="发送信号"></a>发送信号</h2><p>通过kill系统调用发送信号。<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;signal.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">kill</span><span class="params">(<span class="keyword">pid_t</span> pid, <span class="keyword">int</span> sig)</span></span>;</span><br></pre></td></tr></table></figure></p>
<ul>
<li>pid &gt; 0，那么会发送信号给由 pid 指定的进程所属的线程组。</li>
<li>pid = 0，那么会发送信号给与调用进程同组的每个进程，包括调用进程自身。</li>
<li>pid &lt; -1，那么会向组 ID 等于该 pid 绝对值的进程组内所有下属进程发送信号。</li>
<li>pid = -1，调用进程有权将信号发往的每个目标进程，除去swapper(PID 0)，init (PID 1) 和自身进程。</li>
</ul>
<figure class="highlight crystal"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#include &lt;signal.h&gt;</span></span><br><span class="line"></span><br><span class="line">int sigqueue(pid_t pid, int sig, const <span class="class"><span class="keyword">union</span> <span class="title">sigval</span> <span class="title">value</span>);</span></span><br></pre></td></tr></table></figure>
<p>sigqueue系统调用可以发送实时信号。其使用方式同kill，略有不同的是，sigqueue不能通过将pid指定为负值而向整个进程组发送信号。在调用sigaction设置信号处理程序时，可以指定SA_SIGINFO标志位，就能额外收到value数据了。</p>
<h2 id="接收信号"><a href="#接收信号" class="headerlink" title="接收信号"></a>接收信号</h2><p>当内核把进程p从内核态切换到用户态时(例如系统调用返回，或者完成了一次上下文切换(时间片开始处))，会去检查进程p的所有未被阻塞的待处理信号。(pending &amp; ~blocked)，如果这个集合非空，那么内核选择集合中某个信号k(通常是最小的k)，并且强制p接收k。进程p收到k信号后会有如下几种方式对k信号做出应答。收到这个信号会触发某些操作。如果用sigaction注册了信号处理函数，则执行相关信号处理函数，否则执行信号的默认操作。<br>信号默认操作表。</p>
<ol>
<li>显示地忽略信号。</li>
<li>执行信号的缺省操作。由内核定义，详见表xxxx</li>
<li>通过调用相应的信号处理函数捕获信号。</li>
</ol>
<h2 id="阻塞和解除阻塞"><a href="#阻塞和解除阻塞" class="headerlink" title="阻塞和解除阻塞"></a>阻塞和解除阻塞</h2><p>内核会为每个进程维护一个信号掩码，即一组信号，并将阻塞其针对该进程的传递。如果将遭阻塞的信号发送给某进程，那么对该信号的传递将延后，直至从进程信号掩码中移除该信号，从而解除阻塞为止。</p>
<ol>
<li>阻塞当前正在处理的信号。即，如果当前接收到s信号，正在执行S信号处理函数处理，此时又来了一个s信号，则s会变成待处理没有被接收。</li>
<li>调用sigprocmask及其相关函数，明确地阻塞或解除阻塞信号。</li>
</ol>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;signal.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sigprocmask</span><span class="params">(<span class="keyword">int</span> how, <span class="keyword">const</span> <span class="keyword">sigset_t</span> *<span class="keyword">restrict</span> <span class="built_in">set</span>, <span class="keyword">sigset_t</span> *<span class="keyword">restrict</span> oset)</span></span>; <span class="comment">// </span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sigemptyset</span><span class="params">(<span class="keyword">sigset_t</span> *<span class="built_in">set</span>)</span></span>; <span class="comment">// 初始化set为空集合</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sigfillset</span><span class="params">(<span class="keyword">sigset_t</span> *<span class="built_in">set</span>)</span></span>; <span class="comment">// 将每个信号都加到set中</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sigaddset</span><span class="params">(<span class="keyword">sigset_t</span> *<span class="built_in">set</span>, <span class="keyword">int</span> signum)</span></span>; <span class="comment">// 将signum信号添加到set中</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sigdelset</span><span class="params">(<span class="keyword">sigset_t</span> *<span class="built_in">set</span>, <span class="keyword">int</span> signum)</span></span>; <span class="comment">// 从set中删除signum信号</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">sigismember</span><span class="params">(<span class="keyword">const</span> <span class="keyword">sigset_t</span> *<span class="built_in">set</span>, <span class="keyword">int</span> signum)</span></span>; <span class="comment">// 检查signum信号是否在set信号集中。</span></span><br></pre></td></tr></table></figure>
<p>sigprocmask改变当前阻塞的信号集合。取决于<code>how</code>参数的设定。<br>SIG_BLOCK: 将set指向信号集内的指定信号添加到信号掩码中。<br>SIG_UNBLOCK: 将set指向信号集中的信号从信号掩码中移除。即使要解除阻塞的信号当前并未处于阻塞状态，也不会返回错误<br>SIG_SETMASK: 将 set 指向的信号集赋给信号掩码。</p>
<p>如下代码展示如何临时阻塞接收SIGINT<br><figure class="highlight dts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">sigset_t block_set, prev_set;</span><br><span class="line"></span><br><span class="line">sigempty(<span class="variable">&amp;block_set</span>);</span><br><span class="line">sigaddset(<span class="variable">&amp;block_set</span>, SIGINT);</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 阻塞SIGINT，保存先前的信号掩码 */</span></span><br><span class="line">sigprocmask(SIG_BLOCK, <span class="variable">&amp;blcok_set</span>, <span class="variable">&amp;prev_set</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在此执行代码，不会被SIGINT打断。</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/* 恢复先前的信号掩码 */</span></span><br><span class="line">sigprocmask(SIG_SETMASK, <span class="variable">&amp;prev_mask</span>, NULL);</span><br></pre></td></tr></table></figure></p>
<p><a href="https://zhuanlan.zhihu.com/p/258885880" target="_blank" rel="noopener">https://zhuanlan.zhihu.com/p/258885880</a></p>
<h1 id="编写信号处理程序"><a href="#编写信号处理程序" class="headerlink" title="编写信号处理程序"></a>编写信号处理程序</h1><p>信号处理是Linux系统编程最棘手的一个问题。处理程序又几个属性使得它们很难推理分析：</p>
<ul>
<li>处理程序与主程序并发运行，共享同样的全局变量。因此可能与主程序和其他信号处理程序互相干扰</li>
<li>如何以及何时接收信号的规则常常有违人的直觉</li>
<li>不同的系统有不同的信号处理语义</li>
</ul>
<p>图8-31.<br>信号处理程序很麻烦的是因为他们和主程序以及其他信号处理程序并发的执行。如果这些程序都需要访问修改同样的全局变量。将导致不可预知的结果。</p>
<p>《深入理解计算机系统》提出了安全的编写信号处理程序的几个建议：</p>
<ol>
<li>处理程序要尽可能简单。例如，信号处理程序只设置全局标志位并立即返回。</li>
<li>在主程序只调用异步信号安全函数。异步信号安全函数是指那些可重入函数或者不能被信号中断的函数。</li>
<li>保存和恢复errno。许多Linux异步信号安全函数都会在返回时设置errno。在信号处理程序中调用这样的函数会干扰主程序中其他依赖errno的部分。</li>
<li>阻塞所有信号，保护对共享全局数据结构的访问。</li>
<li>用volatile生命全局变量。volatile强制编译器每次在代码中引用被其修饰的变量时都要从内存重新读取。</li>
<li>用sig_atomic_t生命标志。C语言提供整型数据类型sig_atomic_t，对它的读写保证是原子性的。</li>
</ol>
<p>eg： libevent中的信号处理：<br><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">void</span> __cdecl</span><br><span class="line">evsig_handler(<span class="keyword">int</span> sig)</span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">// 保存错误码</span></span><br><span class="line">    <span class="keyword">int</span> save_errno = errno;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> _WIN32</span></span><br><span class="line">    <span class="keyword">int</span> socket_errno = EVUTIL_SOCKET_ERROR();</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">    <span class="keyword">ev_uint8_t</span> msg;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (evsig_base == <span class="literal">NULL</span>) &#123;</span><br><span class="line">        event_warnx(</span><br><span class="line">            <span class="string">"%s: received signal %d, but have no base configured"</span>,</span><br><span class="line">            __func__, sig);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifndef</span> EVENT__HAVE_SIGACTION</span></span><br><span class="line">    signal(sig, evsig_handler);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line"></span><br><span class="line">    <span class="comment">/* Wake up our notification mechanism */</span></span><br><span class="line">    msg = sig;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> _WIN32</span></span><br><span class="line">    send(evsig_base_fd, (<span class="keyword">char</span>*)&amp;msg, <span class="number">1</span>, <span class="number">0</span>);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">else</span></span></span><br><span class="line">    &#123;</span><br><span class="line">    <span class="comment">// 向读端fd，写入一个字节信号值。write是信号安全函数。</span></span><br><span class="line">        <span class="keyword">int</span> r = <span class="built_in">write</span>(evsig_base_fd, (<span class="keyword">char</span>*)&amp;msg, <span class="number">1</span>);</span><br><span class="line">        (<span class="keyword">void</span>)r; <span class="comment">/* Suppress 'unused return value' and 'unused var' */</span></span><br><span class="line">    &#125;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">    errno = save_errno;</span><br><span class="line"><span class="meta">#<span class="meta-keyword">ifdef</span> _WIN32</span></span><br><span class="line">    EVUTIL_SET_SOCKET_ERROR(socket_errno);</span><br><span class="line"><span class="meta">#<span class="meta-keyword">endif</span></span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><br>libevent的信号处理程序遵守了1，2，3规则。信号处理程序很简单，仅仅往一个文件描述符中写入一个字节数据。只调用write信号安全函数，避免调用一些非信号安全的函数。因为write系统调用可能会改变errno，因此保存回复了errno的值。</p>
<p>信号处理时只是记录了下产生了什么信号,并没有进行实际处理,处理过程还是交给主程序本身,避免调用一些非信号安全的函数。因此我们再编写信号处理函数的时候也只要记录下信号状态，对errno这种全局变量，进行保存，处理结束后恢复变量值，尽量避免使用锁。</p>
<p>注：</p>
<ol>
<li>创建一个管道(Libevent实际上使用的是socketpair)</li>
<li>为这个socketpair的一个读端创建一个event，并将之加入到多路IO复用函数的监听之中</li>
<li>设置信号捕抓函数</li>
<li>有信号发生，就往socketpair写入一个字节<br>这里只展示libevent如何处理信号，需更深入了解libevent对信号的处理，可参考如下博客，或libevent源码。<br><a href="https://blog.csdn.net/luotuo44/article/details/38538991" target="_blank" rel="noopener">https://blog.csdn.net/luotuo44/article/details/38538991</a></li>
</ol>
<p>程序。<br><a href="https://www.cnblogs.com/ourroad/p/4834383.html" target="_blank" rel="noopener">https://www.cnblogs.com/ourroad/p/4834383.html</a><br>如图8-31.<br>主程序收到s信号。执行s信号处理程序S。在S执行过程中，收到t信号，t信号终端S程序，转而执行T信号处理程序。T返回时，S从它被中断的地方继续执行。最后S返回，主程序从被中断的地方继续执行。</p>
<p>操作系统内核使用一种成为上下文切换的较高层形式的异常控制流来实现多任务。内核为每个进程维持一个上下文。上下文就是内核重新启动一个被抢占进程所需的状态。<br>上下文切换：1. 保存当前进程的上下文。2. 回复某个先前被抢占的进程被保存的上下文。3. 将控制权传递给这个回复的进程。</p>
<h1 id="多线程程序中使用信号"><a href="#多线程程序中使用信号" class="headerlink" title="多线程程序中使用信号"></a>多线程程序中使用信号</h1><p>本来编写信号处理程序就很难了，多线程程序本身也很难，多线程+信号简直是强人锁男，男上加男嘛。应该尽量避免将两者结合起来使用，不然写出了Bug，就只能一言难尽了。</p>
<h1 id="POSIX信号和多线程应用"><a href="#POSIX信号和多线程应用" class="headerlink" title="POSIX信号和多线程应用"></a>POSIX信号和多线程应用</h1><p>POSIX 1003.1标准对多线程应用的信号处理有一些严格的要求：</p>
<ul>
<li><strong>信号处理程序必须在多线程应用的所有线程之间共享</strong>；不过，每个线程必须有自己的挂起信号掩码和阻塞掩码</li>
<li>POSIX库函数<strong>kill()</strong>和<strong>sigqueue()</strong>必须向<strong>所有</strong>的多线程应用而不是某个特殊的线程发送信号。所有由内核产生的信号同样如此(如：SIGCHLD、SIGINT或SIGQUIT)</li>
<li>每个发送给多线程应用的信号仅传递给一个线程，这个线程是由内核在从<strong>不会阻塞该信号的线程</strong>中随意选择出来的。</li>
<li>如果向多线程应用发送了一个致命的信号，那么内核将<strong>杀死该应用的所有线程</strong>，而不仅仅是杀死接收信号的那个线程。</li>
</ul>
<p>如果一个挂起的信号发送给了某个特定的进程，那么这个信号就是私有的；如果被发送给了整个线程组，它就是共享的。<br>每种信号类型对应一个元素，Linux中可以声明的信号的最大数是64。没有值为0的信号，因此，信号的编号对应于sigset_t类型变量中的相应位下标加1</p>
<h1 id="给指定线程发送信号"><a href="#给指定线程发送信号" class="headerlink" title="给指定线程发送信号"></a>给指定线程发送信号</h1><ol>
<li>pthread_kill类似kill，但pthread_kill给<strong>指定的线程</strong>发送信号。kill会给所有的线程发送信号。</li>
<li>pthread_sigqueue类似sigqueue，但pthread_sigqueue给<strong>指定的线程</strong>发送实时信号。sigqueue会给所有的线程发送实时信号。</li>
</ol>
<p>将信号发给指定线程和发给线程组有很大不同。信号发送给指定线程，由<strong>指定线程</strong>调用信号处理程序对其进行处理；信号发给线程组，且该线程组已然为此信号创建了信号处理程序时，内核会<strong>任选一条线程来接收</strong>这一信号，并在<strong>该线程</strong>中调用信号处理程序对其进行处理。</p>
<h1 id="产生信号"><a href="#产生信号" class="headerlink" title="产生信号"></a>产生信号</h1><p>发送给进程一个信号，这个信号可能来自内核，也可能来自另一个进程。内核通过表所示的某个函数进行调用而产生信号。最后都会调用specific_send_sig_info函数。</p>
<p>当一个信号被发往整个线程组时，这个信号可能来自内核，也可能来自另一个进程。最终都调用group_send_sig_info函数。<br>__group_complete_signal 扫描线程组中的进程，查找能接收新信号的进程。满足以下条件可能被选中。<br>每当从内核态向用户态转换时，内核态堆栈都被清空。</p>
<p>系统调用重新执行。系统调用执行到一半时接收到一个信号，去执行信号处理函数。</p>
<h1 id="发送信号-1"><a href="#发送信号-1" class="headerlink" title="发送信号"></a>发送信号</h1><h1 id="案例分析—localtime-r导致的死锁问题。"><a href="#案例分析—localtime-r导致的死锁问题。" class="headerlink" title="案例分析—localtime_r导致的死锁问题。"></a>案例分析—localtime_r导致的死锁问题。</h1><p>这里分析由我的一个提交引入的死锁问题。</p>
<p>localtime_r是线程安全函数，但不是信号安全函数。进程A执行过程中使用localtime_r，信号处理程序也调用了localtime_r函数localtime_r会调用到tz_convert上锁。如果进程A调用localtime_r已经上锁了，此时进程A收到信号去执行信号处理函数，信号处理函数也调用localtime_r，就会导致死锁发生。<br>如下程序模拟了在信号处理程序中调用localtime_r导致的死锁问题。主线程创建子线程和发送信号线程，注册信号处理程序，在信号处理程序中调用localtime_r函数。子线程处理函数中频繁调用localtime_r。发送信号线程每隔1s给指定线程id的线程发送信号。<br>从实验的结果来看：</p>
<ol>
<li>给子线程发送信号会导致死锁。</li>
<li>给主线程和发送信号线程发送信号不会导致死锁。<br>原因在于，子线程处理程序频繁调用localtime_r函数。信号处理函数也调用localtime_r函数。当子线程处理函数调用localtime_r上锁后还没来得及释放锁，此时收到信号。执行信号处理程序又调用localtime_r请求上锁。从而导致死锁。<br>而主线程和发送信号线程由于没有调用localtime_r，因而给主线程和信号发送线程发送信号不会导致死锁。</li>
</ol>
<figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="class"><span class="keyword">struct</span> <span class="title">tm</span></span> *</span><br><span class="line">localtime_r (<span class="keyword">const</span> time_t *t, <span class="class"><span class="keyword">struct</span> <span class="title">tm</span></span> *tp)</span><br><span class="line">&#123;</span><br><span class="line">  <span class="class"><span class="keyword">struct</span> <span class="title">tm</span></span> *l = localtime (t);</span><br><span class="line">  <span class="keyword">if</span> (! l)</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">  *tp = *l;</span><br><span class="line">  <span class="keyword">return</span> tp;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight rust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">tm</span></span> *</span><br><span class="line">__localtime64_r (<span class="keyword">const</span> __time64_t *t, <span class="class"><span class="keyword">struct</span> <span class="title">tm</span></span> *tp)</span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">return</span> __tz_convert (*t, <span class="number">1</span>, tp);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>pthread_kill发出的信号，只有该线程收到信号并处理。<br>如果<br><a href="https://www.cnblogs.com/coding-my-life/p/4782529.html" target="_blank" rel="noopener">https://www.cnblogs.com/coding-my-life/p/4782529.html</a><br><a href="https://blog.csdn.net/weixin_34067049/article/details/87957176" target="_blank" rel="noopener">https://blog.csdn.net/weixin_34067049/article/details/87957176</a></p>
<h1 id="非信号安全函数带来的风险。死锁风险分析"><a href="#非信号安全函数带来的风险。死锁风险分析" class="headerlink" title="非信号安全函数带来的风险。死锁风险分析"></a>非信号安全函数带来的风险。死锁风险分析</h1><p>死锁概念以及如何避免死锁，已有博客说明，这里不再详细说明。<br>localtime_r带来的死锁风险。<br>测试代码：注：该程序运行在Linux archlinux 5.10.43-1-lts 中<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;pthread.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;signal.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;unistd.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string">&lt;time.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">pthread_t</span> g_main_thread_id;</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">pthread_t</span> g_sub_thread_id;</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">pthread_t</span> g_send_sig_thread_id;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">sig_handler</span><span class="params">(<span class="keyword">int</span> signum)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">time_t</span> time_s = time(<span class="literal">NULL</span>);</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">tm</span> <span class="title">now_time</span>;</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 信号处理程序调用localtime_r非信号安全函数。</span></span><br><span class="line">    localtime_r(&amp;time_s, &amp;now_time);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"get signum %d, the thread_id is %lu\n"</span>, signum, pthread_self());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> *<span class="title">send_sig_func</span><span class="params">(<span class="keyword">void</span> *)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">while</span> (<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">// 发送给main线程处理，不会导致死锁。</span></span><br><span class="line">        <span class="comment">// pthread_kill(g_main_thread_id, SIGUSR1);</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 发送给send_sig线程，不会导致死锁。</span></span><br><span class="line">        <span class="comment">// pthread_kill(g_send_sig_thread_id, SIGUSR1);</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 发送给子线程处理，会导致死锁。子线程处理函数和信号处理都调用了localtime_r</span></span><br><span class="line">        pthread_kill(g_sub_thread_id, SIGUSR1);</span><br><span class="line"></span><br><span class="line">        sleep(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> *<span class="title">call_localtime_r</span><span class="params">(<span class="keyword">void</span> *)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">time_t</span> time_s = time(<span class="literal">NULL</span>);</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">tm</span> <span class="title">now_time</span>;</span></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        localtime_r(&amp;time_s, &amp;now_time);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">(<span class="keyword">void</span>)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="class"><span class="keyword">struct</span> <span class="title">sigaction</span> <span class="title">act</span>;</span></span><br><span class="line">    <span class="keyword">sigset_t</span> block_set;</span><br><span class="line"></span><br><span class="line">    act.sa_flags = <span class="number">0</span>;</span><br><span class="line">    sigemptyset(&amp;act.sa_mask);</span><br><span class="line">    act.sa_handler = sig_handler;</span><br><span class="line">    sigaction(SIGUSR1, &amp;act, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 获取主线程的线程id */</span></span><br><span class="line">    g_main_thread_id = pthread_self();</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"g_main_thread_id = %lu\n"</span>, g_main_thread_id);</span><br><span class="line"></span><br><span class="line">    pthread_create(&amp;g_sub_thread_id, <span class="literal">NULL</span>, call_localtime_r, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"g_sub_thread_id = %lu\n"</span>, g_sub_thread_id);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 发送信号线程 */</span></span><br><span class="line">    pthread_create(&amp;g_send_sig_thread_id, <span class="literal">NULL</span>, send_sig_func, <span class="literal">NULL</span>);</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">"g_send_sig_thread_id = %lu\n"</span>, g_send_sig_thread_id);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/* 注意，省略了错误检查 */</span></span><br><span class="line"></span><br><span class="line">    pthread_join(g_sub_thread_id, <span class="literal">NULL</span>);</span><br><span class="line">    pthread_join(g_send_sig_thread_id, <span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>打印<br><figure class="highlight angelscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">(gdb) thread apply all bt</span><br><span class="line"></span><br><span class="line">Thread <span class="number">3</span> (Thread <span class="number">0x7fcb80b84640</span> (LWP <span class="number">3602</span>) <span class="string">"a.out"</span>): <span class="comment">// g_send_sig_thread_id</span></span><br><span class="line">#<span class="number">0</span>  <span class="number">0x00007fcb8144fa95</span> <span class="keyword">in</span> <span class="symbol">clock_nanosleep@</span>GLIBC_2<span class="number">.2</span><span class="number">.5</span> () <span class="keyword">from</span> /usr/lib/libc.so<span class="number">.6</span></span><br><span class="line">#<span class="number">1</span>  <span class="number">0x00007fcb81454c77</span> <span class="keyword">in</span> nanosleep () <span class="keyword">from</span> /usr/lib/libc.so<span class="number">.6</span></span><br><span class="line">#<span class="number">2</span>  <span class="number">0x00007fcb81454bae</span> <span class="keyword">in</span> sleep () <span class="keyword">from</span> /usr/lib/libc.so<span class="number">.6</span></span><br><span class="line">#<span class="number">3</span>  <span class="number">0x000055c310a54276</span> <span class="keyword">in</span> send_sig_func () at test_localtime_r.c:<span class="number">42</span></span><br><span class="line">#<span class="number">4</span>  <span class="number">0x00007fcb8155e259</span> <span class="keyword">in</span> start_thread () <span class="keyword">from</span> /usr/lib/libpthread.so<span class="number">.0</span></span><br><span class="line">#<span class="number">5</span>  <span class="number">0x00007fcb814875e3</span> <span class="keyword">in</span> clone () <span class="keyword">from</span> /usr/lib/libc.so<span class="number">.6</span></span><br><span class="line"></span><br><span class="line">Thread <span class="number">2</span> (Thread <span class="number">0x7fcb81385640</span> (LWP <span class="number">3601</span>) <span class="string">"a.out"</span>): <span class="comment">// g_sub_thread_id</span></span><br><span class="line">#<span class="number">0</span>  <span class="number">0x00007fcb8140e0ab</span> <span class="keyword">in</span> __lll_lock_wait_private () <span class="keyword">from</span> /usr/lib/libc.so<span class="number">.6</span></span><br><span class="line">#<span class="number">1</span>  <span class="number">0x00007fcb81445fb1</span> <span class="keyword">in</span> __tz_convert () <span class="keyword">from</span> /usr/lib/libc.so<span class="number">.6</span></span><br><span class="line">#<span class="number">2</span>  <span class="number">0x000055c310a54214</span> <span class="keyword">in</span> sig_handler (signum=<span class="number">10</span>) at test_localtime_r.c:<span class="number">25</span></span><br><span class="line">#<span class="number">3</span>  &lt;signal handler called&gt;</span><br><span class="line">#<span class="number">4</span>  <span class="number">0x00007fcb81445940</span> <span class="keyword">in</span> __tzset_parse_tz () <span class="keyword">from</span> /usr/lib/libc.so<span class="number">.6</span></span><br><span class="line">#<span class="number">5</span>  <span class="number">0x00007fcb81447403</span> <span class="keyword">in</span> __tzfile_compute () <span class="keyword">from</span> /usr/lib/libc.so<span class="number">.6</span></span><br><span class="line">#<span class="number">6</span>  <span class="number">0x00007fcb81445e66</span> <span class="keyword">in</span> __tz_convert () <span class="keyword">from</span> /usr/lib/libc.so<span class="number">.6</span></span><br><span class="line">#<span class="number">7</span>  <span class="number">0x000055c310a542b4</span> <span class="keyword">in</span> call_localtime_r () at test_localtime_r.c:<span class="number">52</span></span><br><span class="line">#<span class="number">8</span>  <span class="number">0x00007fcb8155e259</span> <span class="keyword">in</span> start_thread () <span class="keyword">from</span> /usr/lib/libpthread.so<span class="number">.0</span></span><br><span class="line">#<span class="number">9</span>  <span class="number">0x00007fcb814875e3</span> <span class="keyword">in</span> clone () <span class="keyword">from</span> /usr/lib/libc.so<span class="number">.6</span></span><br><span class="line"></span><br><span class="line">Thread <span class="number">1</span> (Thread <span class="number">0x7fcb81386740</span> (LWP <span class="number">3600</span>) <span class="string">"a.out"</span>): <span class="comment">// g_main_thread_id</span></span><br><span class="line">#<span class="number">0</span>  <span class="number">0x00007fcb8156a8ca</span> <span class="keyword">in</span> __futex_abstimed_wait_common64 () <span class="keyword">from</span> /usr/lib/libpthread.so<span class="number">.0</span></span><br><span class="line">#<span class="number">1</span>  <span class="number">0x00007fcb8155f743</span> <span class="keyword">in</span> __pthread_clockjoin_ex () <span class="keyword">from</span> /usr/lib/libpthread.so<span class="number">.0</span></span><br><span class="line">#<span class="number">2</span>  <span class="number">0x000055c310a543d1</span> <span class="keyword">in</span> main () at test_localtime_r.c:<span class="number">79</span></span><br></pre></td></tr></table></figure></p>
<p>glibc中tz_convet代码<br>struct tm <em><br><strong>tz_convert (</strong>time64_t timer, int use_localtime, struct tm </em>tp)<br>{<br>  long int leap_correction;<br>  int leap_extra_secs;</p>
<p>  __libc_lock_lock (tzset_lock);</p>
<p>  /<em> Update internal database according to current TZ setting.<br>     POSIX.1 8.3.7.2 says that localtime_r is not required to set tzname.<br>     This is a good idea since this allows at least a bit more parallelism.  </em>/<br>  tzset_internal (tp == &amp;_tmbuf &amp;&amp; use_localtime);</p>
<p>  if (<strong>use_tzfile)
    </strong>tzfile_compute (timer, use_localtime, &amp;leap_correction,<br>              &amp;leap_extra_secs, tp);<br>  else<br>    {<br>      if (! <strong>offtime (timer, 0, tp))<br>    tp = NULL;<br>      else
    </strong>tz_compute (timer, tp, use_localtime);<br>      leap_correction = 0L;<br>      leap_extra_secs = 0;<br>    }</p>
<p>  __libc_lock_unlock (tzset_lock);</p>
<p>  if (tp)<br>    {<br>      if (! use_localtime)<br>    {<br>      tp-&gt;tm_isdst = 0;<br>      tp-&gt;tm_zone = “GMT”;<br>      tp-&gt;tm_gmtoff = 0L;<br>    }</p>
<pre><code>  if (__offtime (timer, tp-&gt;tm_gmtoff - leap_correction, tp))
    tp-&gt;tm_sec += leap_extra_secs;
  else
tp = NULL;
}
</code></pre><p>  return tp;<br>}</p>
<p>从以上分析就能知道为什么我这个提交会导致死锁了。这个提交，将pthread_kill改成了sigqueue。<br>原本是由xxxx线程执行信号处理程序。变成内核会<strong>任选一条线程来接收</strong>这一信号。main线程没有阻塞xxx信号。很有可能学到main程序来执行信号处理程序。main程序本身也有调用localtime_r，信号处理程序也调用localtime_r。这样就导致了死锁。</p>
<h1 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h1><p>不知道哪位名人说过：未经反省的人生是不值得过的。由于写了一个Bug，因而写了这边博客反省一下。博文若有不正确的地方还请各位同事指出，让我在反省反省~</p>
<h1 id="PyTorch-Code"><a href="#PyTorch-Code" class="headerlink" title="PyTorch Code"></a>PyTorch Code</h1><h1 id="Reference"><a href="#Reference" class="headerlink" title="Reference"></a>Reference</h1><p><a href="">论文地址</a><br><a href="">参考代码</a></p>

    </div>

    
    
    

      <footer class="post-footer">

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2021/05/26/Linux-kernel-module/" rel="prev" title="Linux kernel module">
      <i class="fa fa-chevron-left"></i> Linux kernel module
    </a></div>
      <div class="post-nav-item">
    <a href="/2021/07/08/USB%E7%BD%91%E7%BB%9C%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8/" rel="next" title="USB网络设备驱动">
      USB网络设备驱动 <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  

  </div>


          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let activeClass = CONFIG.comments.activeClass;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          Table of Contents
        </li>
        <li class="sidebar-nav-overview">
          Overview
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#Overview"><span class="nav-number">1.</span> <span class="nav-text">Overview</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#信号"><span class="nav-number">2.</span> <span class="nav-text">信号</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#信号基本概念"><span class="nav-number">3.</span> <span class="nav-text">信号基本概念</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#进程中使用信号"><span class="nav-number">4.</span> <span class="nav-text">进程中使用信号</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#线程中使用信号。"><span class="nav-number">5.</span> <span class="nav-text">线程中使用信号。</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#如何编写合适的信号处理函数"><span class="nav-number">6.</span> <span class="nav-text">如何编写合适的信号处理函数</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#问题"><span class="nav-number">7.</span> <span class="nav-text">问题</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#信号术语"><span class="nav-number">7.1.</span> <span class="nav-text">信号术语</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#发送信号"><span class="nav-number">7.2.</span> <span class="nav-text">发送信号</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#接收信号"><span class="nav-number">7.3.</span> <span class="nav-text">接收信号</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#阻塞和解除阻塞"><span class="nav-number">7.4.</span> <span class="nav-text">阻塞和解除阻塞</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#编写信号处理程序"><span class="nav-number">8.</span> <span class="nav-text">编写信号处理程序</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#多线程程序中使用信号"><span class="nav-number">9.</span> <span class="nav-text">多线程程序中使用信号</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#POSIX信号和多线程应用"><span class="nav-number">10.</span> <span class="nav-text">POSIX信号和多线程应用</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#给指定线程发送信号"><span class="nav-number">11.</span> <span class="nav-text">给指定线程发送信号</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#产生信号"><span class="nav-number">12.</span> <span class="nav-text">产生信号</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#发送信号-1"><span class="nav-number">13.</span> <span class="nav-text">发送信号</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#案例分析—localtime-r导致的死锁问题。"><span class="nav-number">14.</span> <span class="nav-text">案例分析—localtime_r导致的死锁问题。</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#非信号安全函数带来的风险。死锁风险分析"><span class="nav-number">15.</span> <span class="nav-text">非信号安全函数带来的风险。死锁风险分析</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#结语"><span class="nav-number">16.</span> <span class="nav-text">结语</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#PyTorch-Code"><span class="nav-number">17.</span> <span class="nav-text">PyTorch Code</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Reference"><span class="nav-number">18.</span> <span class="nav-text">Reference</span></a></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">Z.J. Jiang</p>
  <div class="site-description" itemprop="description">about the single image super-resolution and face hallucination</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">42</span>
          <span class="site-state-item-name">posts</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">11</span>
        <span class="site-state-item-name">categories</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">1</span>
        <span class="site-state-item-name">tags</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/jzijin" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;jzijin" rel="noopener" target="_blank"><i class="fa fa-fw fa-github"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="/atom.xml" title="RSS → &#x2F;atom.xml"><i class="fa fa-fw fa-rss"></i>RSS</a>
      </span>
  </div>


  <div class="links-of-blogroll motion-element">
    <div class="links-of-blogroll-title">
      <i class="fa fa-fw fa-link"></i>
      Links
    </div>
    <ul class="links-of-blogroll-list">
        <li class="links-of-blogroll-item">
          <a href="http://www.njust.edu.cn/" title="http:&#x2F;&#x2F;www.njust.edu.cn" rel="noopener" target="_blank">南京理工大学</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="http://ehall.njust.edu.cn/new/index.html" title="http:&#x2F;&#x2F;ehall.njust.edu.cn&#x2F;new&#x2F;index.html" rel="noopener" target="_blank">南京理工大学智慧服务</a>
        </li>
        <li class="links-of-blogroll-item">
          <a href="http://lib.njust.edu.cn/" title="http:&#x2F;&#x2F;lib.njust.edu.cn&#x2F;" rel="noopener" target="_blank">南京理工大学图书馆</a>
        </li>
    </ul>
  </div>

      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2022</span>
  <span class="with-love">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Z.J. Jiang</span>
</div>
  <div class="powered-by">Powered by <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> v4.2.0
  </div>
  <span class="post-meta-divider">|</span>
  <div class="theme-info">Theme – <a href="https://pisces.theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Pisces</a> v7.6.0
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  















  

  
      
<script type="text/x-mathjax-config">
    MathJax.Ajax.config.path['mhchem'] = '//cdn.jsdelivr.net/npm/mathjax-mhchem@3';

  MathJax.Hub.Config({
    tex2jax: {
      inlineMath: [ ['$', '$'], ['\\(', '\\)'] ],
      processEscapes: true,
      skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
    },
    TeX: {
        extensions: ['[mhchem]/mhchem.js'],
      equationNumbers: {
        autoNumber: 'AMS'
      }
    }
  });

  MathJax.Hub.Register.StartupHook('TeX Jax Ready', function() {
    MathJax.InputJax.TeX.prefilterHooks.Add(function(data) {
      if (data.display) {
        var next = data.script.nextSibling;
        while (next && next.nodeName.toLowerCase() === '#text') {
          next = next.nextSibling;
        }
        if (next && next.nodeName.toLowerCase() === 'br') {
          next.parentNode.removeChild(next);
        }
      }
    });
  });

  MathJax.Hub.Queue(function() {
    var all = MathJax.Hub.getAllJax(), i;
    for (i = 0; i < all.length; i += 1) {
      element = document.getElementById(all[i].inputID + '-Frame').parentNode;
      if (element.nodeName.toLowerCase() == 'li') {
        element = element.parentNode;
      }
      element.classList.add('has-jax');
    }
  });
</script>
<script>
  NexT.utils.getScript('//cdn.jsdelivr.net/npm/mathjax@2/MathJax.js?config=TeX-AMS-MML_HTMLorMML', () => {
    MathJax.Hub.Typeset();
  }, window.MathJax);
</script>

    

  

</body>
</html>
